/* DTerminErzeugung.java
 *
 * Author:	Malcolm Staudacher
 * Date:	May 1, 2008
 *
 */

package ocumed.applikation.domainhandling;

import java.util.Calendar;
import java.util.Date;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;

import ocumed.applikation.exceptions.BenutzerNotFoundException;
import ocumed.applikation.exceptions.NoArztException;
import ocumed.domaene.benutzerverwaltung.arztverwaltung.DArzt;
import ocumed.domaene.patientenverwaltung.DPatient;
import ocumed.domaene.terminverwaltung.DTermin;
import ocumed.domaene.terminverwaltung.DTerminArt;
import ocumed.domaene.terminverwaltung.DTerminkalender;
import ocumed.persistenz.dao.BenutzerDAO;
import ocumed.persistenz.dao.DAOFactory;
import ocumed.persistenz.dao.FehlzeitDAO;
import ocumed.persistenz.dao.GruppeDAO;
import ocumed.persistenz.dao.OrdinationDAO;
import ocumed.persistenz.dao.PatientDAO;
import ocumed.persistenz.dao.PatientloginDAO;
import ocumed.persistenz.dao.TerminDAO;
import ocumed.persistenz.dao.TerminartDAO;
import ocumed.persistenz.hibernate.OcBenutzer;
import ocumed.persistenz.hibernate.OcPatient;
import ocumed.persistenz.hibernate.OcPatientlogin;
import ocumed.persistenz.hibernate.OcTermin;
import ocumed.persistenz.hibernate.OcTerminart;
import ocumed.teams.ITermin;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Class for creating domain objects for all domain classes in package
 * ocumed.domaene.terminverwaltung
 * 
 * @date May 1, 2008
 * @author Malcolm Staudacher
 * @author Daniel Rudigier
 * 
 */
public class TerminErzeugung {

	private static final Log log = LogFactory.getLog(TerminErzeugung.class);

	// access the domain handler
	private DomainHandler dh;

	// DAO interfaces
	protected BenutzerDAO m_benutzerDAO;
	protected GruppeDAO m_gruppeDAO;
	protected OrdinationDAO m_ordinationDAO;
	protected FehlzeitDAO m_fehlzeitDAO;
	protected TerminDAO m_terminDAO;
	protected TerminartDAO m_terminArtDAO;
	protected PatientDAO m_patientDAO;
	protected PatientloginDAO m_patientloginDAO;


	/**
	 * constructor
	 *
	 */
	public TerminErzeugung() {
		dh = DomainHandler.getInstance();

		m_fehlzeitDAO = DAOFactory.get(DAOFactory.RDBMS).getFehlzeitDAO();
		m_terminDAO = DAOFactory.get(DAOFactory.RDBMS).getTerminDAO();
		m_terminArtDAO = DAOFactory.get(DAOFactory.RDBMS).getTerminartDAO();
		m_benutzerDAO = DAOFactory.get(DAOFactory.RDBMS).getBenutzerDAO();
		m_patientDAO = DAOFactory.get(DAOFactory.RDBMS).getPatientDAO();
		m_ordinationDAO = DAOFactory.get(DAOFactory.RDBMS).getOrdinationDAO();
		m_gruppeDAO = DAOFactory.get(DAOFactory.RDBMS).getGruppeDAO();
		m_patientloginDAO = DAOFactory.get(DAOFactory.RDBMS).getPatientloginDAO();
	}


	/**
	 * Gets the DTerminArt domain object for the specified data object.
	 * 
	 * @param ocTerminArt
	 *            The data object.
	 * @return The domain object.
	 */
	public DTerminArt getDTerminArt(OcTerminart ocTerminArt) {
		DTerminArt ta = getTA(ocTerminArt.getTerminartid());

		if (ta != null) {
			return ta;
		}

		ta = new DTerminArt(ocTerminArt.getTerminartid(), ocTerminArt
				.getTerminartbezeichnung(), ocTerminArt.getTerminartdauer(),
				ocTerminArt.getTerminartmitvorbehandlung(), ocTerminArt
						.getTerminartiststandard());
		dh.getTerminArten().add(ta);
		return ta;
	}


	/**
	 * Gets the DTerminArt domain object with the specified ID.
	 * 
	 * @param id
	 *            The ID.
	 * @return The domain object.
	 */
	public DTerminArt getDTerminArt(int id) {
		m_terminArtDAO.start();
		OcTerminart art = m_terminArtDAO.findById(id);
		return getDTerminArt(art);
	}


	/**
	 * Gets the DTermin domain object for the specified appointment ID.
	 * 
	 * @param terminId
	 *            The appointment ID.
	 * @return The corresponding domain object.
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DTermin getDTermin(int terminId) throws NoArztException,
			BenutzerNotFoundException {

		m_terminDAO.start();
		OcTermin ocT = m_terminDAO.findById(terminId);
		
		if (ocT == null) {
		    return null;
		}
		
		return getDTermin(ocT);
	}


	/**
	 * Gets the DTermin domain object for the specified OcTermin data object.
	 * 
	 * @param ocTermin
	 *            The data object.
	 * @param arzt
	 *            The doctor, who this appointment will be assigned to.
	 * @return The corresponding domain object.
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DTermin getDTermin(OcTermin ocTermin, DArzt arzt)
			throws NoArztException, BenutzerNotFoundException {
		DTermin t = getT(ocTermin.getTerminid());

		if (t != null) {
			return t;
		}

		// load default references
		DTerminArt art = getDTerminArt(ocTermin.getOcTerminart());
		DPatient patient = DomainHandler.pe.getDPatient(ocTermin
				.getOcPatient(), arzt);

		// translate the database value
		boolean mitV = ocTermin.getTerminmitvorbehandlung() == TerminDAO.mitVorbehandlung;
		boolean wahrgen = ocTermin.getTerminwahrgenommen() == TerminDAO.terminWahrgenommen;

		t = new DTermin(ocTermin.getTerminid(), arzt, art, patient, ocTermin
				.getTerminzeitpunkt(), ocTermin.getTermindauer(), mitV,
				wahrgen, ocTermin.getTermintext());
		dh.getTermine().add(t);
		return t;
	}


	/**
	 * Gets the DTermin domain object to the specified OcTermin data object.
	 * 
	 * @param ocTermin
	 *            The data object.
	 * @return The corresponding domain object.
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DTermin getDTermin(OcTermin ocTermin) throws NoArztException,
			BenutzerNotFoundException {
		DTermin t = getT(ocTermin.getTerminid());

		if (t != null) {
			return t;
		}

		// load default doctor
		DArzt arzt = DomainHandler.be.getDArzt(ocTermin.getOcBenutzer());

		return getDTermin(ocTermin, arzt);
	}


	/**
	 * Gets the calendar of the specified doctor. If the domain object does not
	 * exist, it will be created.
	 * 
	 * @param arztId
	 *            The doctor, whose calendar to get.
	 * @return the calendar of the specified doctor.
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DTerminkalender getDTerminKalender(int arztId)
			throws NoArztException, BenutzerNotFoundException {
		DTerminkalender tk = getK(arztId);

		if (tk != null) {
			return tk;
		}

		// lookup the reference
		m_benutzerDAO.start();
		OcBenutzer ocArzt = m_benutzerDAO.findById(arztId);
		DArzt arzt = DomainHandler.be.getDArzt(ocArzt);

		// use real method
		return getDTerminKalender(arzt);
	}


	/**
	 * get all domain appointments for a doctor. force refresh.
	 * 
	 * @param arzt     provided doctor
	 * @return all assigned appointments
	 * @throws BenutzerNotFoundException 
	 * @throws NoArztException 
	 */
	public Vector<DTermin> getDTermine(DArzt arzt) throws NoArztException,
			BenutzerNotFoundException {
		log.debug("request new appointments for " + arzt.getBenutzerid());

		m_benutzerDAO.start();
		OcBenutzer ocArzt = m_benutzerDAO.findById(arzt.getBenutzerid());

		Vector<DTermin> arztTermine = new Vector<DTermin>();
		for (OcTermin t : ocArzt.getOcTermins()) {
			arztTermine.add(getDTermin(t, arzt));
		}

		log.debug("found: " + arztTermine.size());
		return arztTermine;
	}


	/**
	 * Gets the calendar of the specified doctor. If the domain object does not
	 * exist, it will be created.
	 * 
	 * @param arzt
	 *            The doctor, whose calendar to get.
	 * @return the calendar of the specified doctor.
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DTerminkalender getDTerminKalender(DArzt arzt)
			throws NoArztException, BenutzerNotFoundException {
        log.debug("requesting calender for doctor: # " + arzt.getBenutzerid());
        
		DTerminkalender tk = getK(arzt.getBenutzerid());
		if (tk != null) {
			return tk;
		}

		// force a refresh on the existing data
		Vector<DTermin> arztTermine = getDTermine(arzt);

		tk = new DTerminkalender(arztTermine);
		dh.getKalender().add(tk);
		return tk;
	}


	/**
	 * Gets all appointments for the specified patient.
	 * 
	 * @param PatientId
	 * @return A Vector of appointments.
	 */
	public Set<ITermin> getDTermine(OcPatient p) {
		Set<ITermin> dts = new TreeSet<ITermin>();
		
		for (OcTermin oct : p.getOcTermins()) {
			dts.add(getDTermin(oct));
		}
		
		return dts;		
	}


	/**
	 * Writes a DTermin domain object to the database.
	 * 
	 * @param t
	 *            The domain object to persist.
	 * @return true, if the object was successfully persisted, false otherwise.
	 */
	public boolean persist(DTermin t) {
		// set status to 'dirty', so that this appointment will be loaded from
		// the database
		t.setDirty(true);

		m_terminDAO.start();
		OcTermin termin = new OcTermin();

		m_patientDAO.start();
		OcPatient patient = m_patientDAO
				.findById(t.getPatient().getPatientid());

		m_benutzerDAO.start();
		OcBenutzer arzt = m_benutzerDAO.findById(t.getArzt().getBenutzerid());

		m_terminArtDAO.start();
		OcTerminart art = m_terminArtDAO.findById(t.getTerminart().getId());

		termin.setOcPatient(patient);
		termin.setOcBenutzer(arzt);
		termin.setOcTerminart(art);
		termin.setTerminzeitpunkt(t.getTerminZeitpunkt());
		termin.setTermindauer(t.getTermindauer());

		char vorbehandlung = t.getTerminmitvorbehandlung() ? TerminDAO.mitVorbehandlung
				: TerminDAO.ohneVorbehandlung;
		termin.setTerminmitvorbehandlung(vorbehandlung);

		termin.setTerminwahrgenommen(TerminDAO.terminNichtWahrgenommen);
		termin.setTermintext(t.getTermintext());

		// do it now
		try {
			m_terminDAO.start();
			Integer id = m_terminDAO.persist(termin);
			m_terminDAO.commit();
			termin.setTerminid(id);

			// register in the brain
			log.debug("register termin in brain: " + id);
			t.setTerminid(id);
			DomainHandler.te.getDTerminKalender(t.getArzt())
					.terminHinzufuegen(t);
		} catch (RuntimeException re) {
			re.printStackTrace();
			return false;
		}

		return true;
	}


	/**
	 * cache lookup 
	 * @param id
	 * @return
	 */
	private DTerminArt getTA(int id) {
		for (DTerminArt ta : dh.getTerminArten()) {
			if (ta.getId() == id) {
				return ta;
			}
		}

		return null;
	}


	/**
	 * cache lookup
	 * 
	 * @param id
	 * @return
	 */
	private DTermin getT(int id) {
		for (DTermin t : dh.getTermine()) {
			// only get from database if status is not dirty
			if (t.getTerminid() == id && !t.isDirty()) {
				return t;
			}
		}

		return null;
	}


	/**
	 * cache lookup
	 * 
	 * @param arztId
	 * @return
	 */
	private DTerminkalender getK(int arztId) {
		for (DTerminkalender tk : dh.getKalender()) {
			if (tk.getArzt().getBenutzerid() == arztId) {
				return tk;
			}
		}

		return null;
	}

}
